<!DOCTYPE html>
<html>
  <head>
    <title>WebGL Hello World!</title>
  </head>

  <body>
    <body onload="main()">
      <canvas id="glcanvas" width="640" height="480">
        你的浏览器似乎不支持或者禁用了HTML5 <code>&lt;canvas&gt;</code> 元素.
      </canvas>
    </body>
    <script type="x-shader/x-vertex">
      //  初始化着色器程序，让WebGL知道如何绘制我们的数据
      function initShaderProgram(gl, vsSource, fsSource) {
          const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
          const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);

          // 创建着色器程序

          const shaderProgram = gl.createProgram();
          gl.attachShader(shaderProgram, vertexShader);
          gl.attachShader(shaderProgram, fragmentShader);
          gl.linkProgram(shaderProgram);

          // 创建失败， alert
          if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
              alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
              return null;
          }

          return shaderProgram;
      }

      //
      // 创建指定类型的着色器，上传source源码并编译
      //
      function loadShader(gl, type, source) {
          const shader = gl.createShader(type);

          // Send the source to the shader object

          gl.shaderSource(shader, source);

          // Compile the shader program

          gl.compileShader(shader);

          // See if it compiled successfully

          if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
              alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
              gl.deleteShader(shader);
              return null;
          }

          return shader;
          }
          const shaderProgram = initShaderProgram(gl, vsSource, fsSource);
          const programInfo = {
            program: shaderProgram,
            attribLocations: {
            vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
            },
            uniformLocations: {
            projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
            modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'),
            },
          };
      }
      function initBuffers(gl) {
        const positionBuffer = gl.createBuffer();
        const positions = [
            1.0,  1.0,
            -1.0,  1.0,
            1.0, -1.0,
            -1.0, -1.0,
        ];
        gl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, positionBuffer);
        gl.bufferData(WebGLRenderingContext.ARRAY_BUFFER,
            new Float32Array(positions),
            WebGLRenderingContext.STATIC_DRAW
        );

        const colorBuffer = gl.createBuffer();
        const colors = [
            1.0,  1.0,  1.0,  1.0,    // 白
            1.0,  0.0,  0.0,  1.0,    // 红
            0.0,  1.0,  0.0,  1.0,    // 绿
            0.0,  0.0,  1.0,  1.0,    // 蓝
        ];

        gl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, colorBuffer);
        gl.bufferData(WebGLRenderingContext.ARRAY_BUFFER,
            new Float32Array(colors),
            WebGLRenderingContext.STATIC_DRAW
        );

        return {
            position: positionBuffer,
            color: colorBuffer,
        };
      }
      function drawScene(gl, programInfo, buffers) {
          gl.clearColor(0.0, 0.0, 0.0, 1.0);  // Clear to black, fully opaque
          gl.clearDepth(1.0);                 // Clear everything
          gl.enable(gl.DEPTH_TEST);           // Enable depth testing
          gl.depthFunc(gl.LEQUAL);            // Near things obscure far things

          // Clear the canvas before we start drawing on it.

          gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

          // Create a perspective matrix, a special matrix that is
          // used to simulate the distortion of perspective in a camera.
          // Our field of view is 45 degrees, with a width/height
          // ratio that matches the display size of the canvas
          // and we only want to see objects between 0.1 units
          // and 100 units away from the camera.

          const fieldOfView = 45 * Math.PI / 180;   // in radians
          const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
          const zNear = 0.1;
          const zFar = 100.0;
          const projectionMatrix = mat4.create();

          // note: glmatrix.js always has the first argument
          // as the destination to receive the result.
          mat4.perspective(projectionMatrix,
                          fieldOfView,
                          aspect,
                          zNear,
                          zFar);

          // Set the drawing position to the "identity" point, which is
          // the center of the scene.
          const modelViewMatrix = mat4.create();

          // Now move the drawing position a bit to where we want to
          // start drawing the square.

          mat4.translate(modelViewMatrix,     // destination matrix
                          modelViewMatrix,     // matrix to translate
                          [-0.0, 0.0, -6.0]);  // amount to translate

          // Tell WebGL how to pull out the positions from the position
          // buffer into the vertexPosition attribute.
          {
              const numComponents = 3;  // pull out 3 values per iteration
              const type = gl.FLOAT;    // the data in the buffer is 32bit floats
              const normalize = false;  // don't normalize
              const stride = 0;         // how many bytes to get from one set of values to the next
                                      // 0 = use type and numComponents above
              const offset = 0;         // how many bytes inside the buffer to start from
              gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
              gl.vertexAttribPointer(
                  programInfo.attribLocations.vertexPosition,
                  numComponents,
                  type,
                  normalize,
                  stride,
                  offset);
              gl.enableVertexAttribArray(
                  programInfo.attribLocations.vertexPosition);
          }

          // Tell WebGL to use our program when drawing

          gl.useProgram(programInfo.program);

          // Set the shader uniforms

          gl.uniformMatrix4fv(
              programInfo.uniformLocations.projectionMatrix,
              false,
              projectionMatrix);
          gl.uniformMatrix4fv(
              programInfo.uniformLocations.modelViewMatrix,
              false,
              modelViewMatrix);

          {
              const offset = 0;
              const vertexCount = 4;
              gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount);
          }
      }
      function main() {
          const canvas = document.querySelector("#glcanvas");
          // 初始化WebGL上下文
          const gl = canvas.getContext("webgl");

          // 确认WebGL支持性
          if (!gl) {
              alert("无法初始化WebGL，你的浏览器、操作系统或硬件等可能不支持WebGL。");
              return;
          }
          // 顶点着色器
          // 将输入顶点（aVertexPosition）通过与w2c矩阵和投影矩阵相乘，转换为可以实际在屏幕上显示的像素。
          // 下面是GLSL语言的源代码
        const vsSource = `
            attribute vec3 aVertexPosition;
            attribute vec4 aVertexColor;

            uniform mat4 uModelViewMatrix;
            uniform mat4 uProjectionMatrix;

            varying lowp vec4 vColor;

            void main(void) {
                gl_Position = uProjectionMatrix* uModelViewMatrix * vec4(aVertexPosition, 1.0);
                vColor = aVertexColor;
            }
        `;
          // 片段着色器
          // 对每个要被绘制的像素点调用一次，用来确定像素颜色。
          // 获取纹理元素颜色，然后应用光照来确认最终颜色。
          const fsSource = `
            varying lowp vec4 vColor;
            void main(void) {
                gl_FragColor = vColor;
            }
          `;
          // 将这两个着色器传递给WebGL，编译并连接。
          const shaderProgram = initShaderProgram(gl, vsSource, fsSource);
          // 将attribute和uniform放到一起，便于传递。
          // attribute是输入，uniforms类似全局变量
          const programInfo = {
                program: shaderProgram,
                attribLocations: {
                vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
                },
                uniformLocations: {
                projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
                modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'),
                },
          };
          // 创建缓冲器，包含顶点信息。
          const buffers = initBuffers(gl);
          drawScene(gl, programInfo, buffers)
      }
    </script>
  </body>
</html>
